package com.qs.sckill.controller;

import com.qs.sckill.config.redis.key.GoodsKey;
import com.qs.sckill.entity.OrderInfo;
import com.qs.sckill.entity.SckillOrder;
import com.qs.sckill.entity.User;
import com.qs.sckill.result.CodeMsg;
import com.qs.sckill.result.Result;
import com.qs.sckill.service.*;
import com.qs.sckill.vo.GoodsVo;
import com.qs.sckill.vo.SckillMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import java.util.List;

@Controller
@RequestMapping("/sckill")
public class SckillController implements InitializingBean {

    @Resource
    private UserService userService;

    @Resource
    private GoodsService goodsService;

    @Resource
    private OrderService orderService;

    @Resource
    private SckillService sckillService;

    @Autowired
    private AmqpTemplate amqpTemplate;


    @Autowired
    private RedisService redisService;

    /**
     * 1、系统初始化时(spring注入bean到容器时候)，将秒杀的库存在redis中，秒杀时减库存，直接使用redis的decr命令操作
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        //获取所有的秒杀商品列表
        List<GoodsVo> goodsList = goodsService.listGoodsVo();
        if (goodsList == null) {
            return;
        }

        for (GoodsVo goods : goodsList) {
            String goodsId = goods.getId();
            Integer stockCount = goods.getStockCount();
            //如果缓存中不存在库存，则将库存存储redis中（集群环境下是否会有并发问题？）
            if (StringUtils.isBlank(redisService.get(GoodsKey.sckillGoodsStock, goodsId, String.class))) {
                redisService.set(GoodsKey.sckillGoodsStock, goodsId, String.valueOf(stockCount));
            }
        }
    }

    @PostMapping(value = "/doSckill")
    @ResponseBody
    public Result<Integer> doSckill(User user, @RequestParam("goodsId") String goodsId) {
        //用户未登录
        if (user == null) {
            return Result.error(CodeMsg.USER_NOT_LOGIN);
        }

        //1、获取redis中库存，判断库存是否足够，库存不够了，直接返回；如果库存足够，则redis中递减库存；
        String stockCount = redisService.get(GoodsKey.sckillGoodsStock, goodsId, String.class);
        if (StringUtils.isBlank(stockCount)) {
            stockCount = String.valueOf(goodsService.getStockCountByGoodsId(goodsId));
            redisService.set(GoodsKey.sckillGoodsStock, goodsId, stockCount);
        }

        Long remainStockCount = redisService.decr(GoodsKey.sckillGoodsStock, goodsId);
        if (remainStockCount < 0) {
            return Result.error(CodeMsg.SERVER_ERROR);
        }

        //2、判断是否秒杀到该商品
        SckillOrder sckillOrder = orderService.getSckillOrderByUserIdAndGoodsId(user.getId(), goodsId);
        if (sckillOrder != null) {
            return Result.error(CodeMsg.REPEAT_SCKILL);
        }

        //3、将该用户的秒杀请求，入队列，发送消息，直接返回排队中；
        SckillMessage sckillMessage = new SckillMessage();
        sckillMessage.setGoodsId(goodsId);
        sckillMessage.setUser(user);
        //将秒杀请求信息直接存储在消息队列
        sendMsg(sckillMessage);
        return Result.success(0);
    }

    //使用RabbitMQ发送消息
    public void sendMsg(SckillMessage sckillMessage) {
        try {
            String sckillExchange = "goods.sckill.exchange";
            amqpTemplate.convertAndSend(sckillExchange, "goods.doSckill", sckillMessage);
        } catch (AmqpException e) {
            e.printStackTrace();
        }
    }


    @PostMapping(value = "/getSckillResult")
    @ResponseBody
    public Result<String> getSckillResult(User user, String goodsId) {
        //用户未登录
        if (user == null) {
            return Result.error(CodeMsg.USER_NOT_LOGIN);
        }

        //获取用户订单号(可能是状态码，"-1"表示库存不足，秒杀失败；"0"表示暂时还未获取到消息队列的处理成功结果；)
        String result = orderService.getGoodsSckillResult(user.getId(), goodsId);
        return Result.success(result);
    }

    /**
     * 秒杀（旧）
     * <p>
     * 备注：经过jmeter压力测试，出现的问题如下：
     * 1、同时秒杀商品，可能出现产品超卖的情况。
     * 2、秒杀接口性能不高。
     */
    @RequestMapping("/do_sckill1")
    public String doSckill1(Model model, User user, @RequestParam("goodsId") String goodsId) {
        if (user == null) {
            return "redirect:/login/to_login";
        }

        model.addAttribute("user", user);

        //判断库存，库存不够则表示秒杀已经结束
        GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
        int stock = goods.getGoodsStock();
        if (stock <= 0) {
            model.addAttribute("errmsg", CodeMsg.SCKILL_OVER.getMsg());
            return "sckill_fail";
        }

        //判断是否已经秒杀到了（先从redis中查询缓存，缓存不存在，则查询数据库）
        SckillOrder order = orderService.getSckillOrderByUserIdAndGoodsId(user.getId(), goodsId);
        if (order != null) {
            model.addAttribute("errmsg", CodeMsg.REPEAT_SCKILL.getMsg());
            return "sckill_fail";
        }

        //减库存、下订单、写入秒杀
        OrderInfo orderInfo = null;
        try {
            orderInfo = sckillService.doSckill(user, goods);
            model.addAttribute("orderInfo", orderInfo);
            model.addAttribute("goods", goods);
            return "order_detail";
        } catch (Exception e) {
            System.out.println("秒杀接口出现异常，可能是多线程环境下，插入订单信息重复，违法数据库唯一约束" + e.getMessage());
            model.addAttribute("errmsg", CodeMsg.SCKILL_ERROR.getMsg());
            return "sckill_fail";
        }
    }
}
